package control;


import java.util.Arrays;
import java.util.Hashtable;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import cliente_admin.ClienteAdmin;
import cliente_admin.ModuloConsultas;
import common.Mensaje;
import common.MensajeConsulta;
import componentesAux.CreadorTablas;
import componentesAux.LectorIpFile;
import componentesAux.ParsearRtdos;

/**
 * Clase que está asociada al archivo FXML que establece el layout de la gui de la aplicación del cliente admin.
 * En su gran mayoría son oyentes de los botones que se crean en el archivo  "Gui.fxml", además de algunos métodos
 * que inicializan parte de algún panel.
 * Los atributos que están precedidos por un "@FXML" indican que es un componente de la gui en el archivo FXML,
 * mediante este tag puede manipularse desde esta clase.
 * Los método que estan precedidos por el tag "@FXML" indican que es un método que es invocado desde el archivo FXML (son
 * oyentes de componentes de la gui).
 * @author Matias
 *
 */
public class Control {
	private static final int FRECUENCIA_ACTUALIZACION_PANEL_ASAMBLEISTAS=2500;

	@FXML private StackPane stackPane;
	@FXML private GridPane grillaSesion;
	@FXML private Button botonGrilla;
	@FXML private Label l_cant_actual;
	// componentes del panel de login
	@FXML private Pane p_login;
	@FXML private PasswordField pass_field;
	@FXML private Label l_login;


	@FXML private AnchorPane p_admin; // panel con las pestañas y eso
	// componentes del panel de control
	@FXML private Pane p_bienvenidos;
	@FXML private Pane p_aceptando;
	@FXML private Pane p_nuevo_tema;
	@FXML private Pane p_iniciar_votacion;
	@FXML private Pane p_en_votacion;
	@FXML private Pane p_rtdos;
	@FXML private Pane p_no_hay_quorum;

	@FXML private Button b_salir;
	@FXML private VBox botones_rtdos;
	@FXML private Button b_cerrar_sesion;
	@FXML private Button b_nuevo_tema;
	@FXML private Label l_comment_rtdos;

	@FXML private TextField tf_tema;

	@FXML private Label l_tema_actual_iv;
	@FXML private Label l_tema_actual_ev;
	@FXML private Label l_tema_actual_r;

	@FXML private Label l_rtdos_p;
	@FXML private Label l_rtdos_n;
	@FXML private Label l_rtdos_a; 

	// elementos del panel de consultas
	@SuppressWarnings("rawtypes")
	@FXML private TableView tabla_rtdos;
	@FXML private ChoiceBox<EntradaCBox> cbox_tema;
	@FXML private ChoiceBox<EntradaCBox> cbox_sesion;
	@FXML private VBox vbox_consultas;

	private String tema_actual;
	private boolean flag_cierre_sesion;

	// elementos no propios de la GUI
	private ClienteAdmin admin;
	private ModuloConsultas moduloConsultas;

	// mapeo para las consultas
	private Map<String,EntradaCBox> mapeoSesiones;
	private Map<String,EntradaCBox> mapeoTemas;
	private CreadorTablas creadorTablas;
	@FXML private AnchorPane panelTabla;
	@FXML private Label l_consultas_titulo;

	private static Label[] arregloLabel=new Label[72];
	private static Pane[] arregloPaneles=new Pane[72];
	private static ImageView[] arregloIconoVoto=new ImageView[72];

	// ----- COMPONENTES DEL PANEL DE OPCIONES -----
	@FXML private Pane p_opciones;
	@FXML private PasswordField pf_opciones_pass_actual;
	@FXML private PasswordField pf_opciones_pass_nuevo1;
	@FXML private PasswordField pf_opciones_pass_nuevo2;
	@FXML private Button b_opciones_cambiar_pass;
	@FXML private Label l_opciones_msj_pass;

	@FXML private TextField tf_opciones_ip;
	@FXML private Button b_opciones_cambiar_ip;
	@FXML private Label l_opciones_cambiar_ip;

	@FXML private TilePane grillaAsambleistas;

	@FXML private AnchorPane p_error;

	@FXML private PieChart grafico_rtdos;

	private String pass_valido;
	
	/**
	 * Constructor de la clase, es lo primero que ejecuta al cargar la gui 
	 */
	public Control(){
		admin = new ClienteAdmin(this);
		flag_cierre_sesion = false;
		creadorTablas = new CreadorTablas();

	}
	
	/**
	 * Método que actualiza el panel donde se muestran los estados de las conexiones de los asambleistas.
	 */
	private void actualizarPanelAsambleistas(){
		Runnable run = new Runnable(){
			public void run(){
				boolean flag_cerrar=false;
				String rta_previa=null;
				String rta_actual;
				while(!flag_cerrar){
					Mensaje msj = new Mensaje();
					msj.setTipo_msj(Mensaje.TIPO_CANT_ASAMBLEISTAS);
					Mensaje rta = admin.enviarMSJ(msj);

					if(rta.getTipo_msj().equals(Mensaje.TIPO_OK)){
						rta_actual = rta.getContenido();
						if(!rta_actual.equals(rta_previa))
							mostrarNombres(rta_actual);
						rta_previa=rta_actual;
					}else if(rta.getTipo_msj().equals(Mensaje.TIPO_CIERRE_SESION)){
						flag_cerrar=true;
					}else{
						// no se que hacemos
					}
					// frecuencia de actualizacion
					try {Thread.sleep(FRECUENCIA_ACTUALIZACION_PANEL_ASAMBLEISTAS);} catch (InterruptedException e) {}
				}
			}
		};
		Thread t=new Thread(run);
		admin.setThread_invocante_actual(t);
		t.start();
	}
	
	/**
	 * Método que recorre los nombres del msj con el estado de las conexiones proveniente del servidor y
	 * actualiza los labels del panel donde se muestran los asableistas conectados
	 * @param nombres : msj proveniente del servidor con el estado de las conexiones de todos los clientes
	 */
	private void mostrarNombres(String nombres){
		String[] nbres_spliteados = nombres.split("-");
		final String desconectado="[DESCONECTADO]";
		final String voto="[VOTO]";
		final int truncLength=desconectado.length();
		for(int i=0; i<nbres_spliteados.length;i++){
			final int valor=i;
			final String nbre=nbres_spliteados[i];
			
			try{Thread.sleep(30);}catch(Exception e){};// <<<<<<<< esto demora el refresh
			Platform.runLater(new Runnable(){
				public void run(){
					if(nbre.contains(desconectado)){ // asambleista que estaba conectado pero se desconecto
						arregloLabel[valor].setText(nbre.substring(truncLength));
						arregloLabel[valor].setTextFill(Color.WHITE);
						arregloPaneles[valor].setStyle("-fx-background-color: red;-fx-border-color: black;");
						arregloIconoVoto[valor].setVisible(false);
						
					}else if(nbre.contains(voto)){ // asambleista que voto para el tema actual
						arregloLabel[valor].setText(nbre.substring(voto.length()));
						arregloLabel[valor].setTextFill(Color.YELLOW);
						//arregloPaneles[valor].setStyle("-fx-background-color: blue;-fx-border-color: black;");
						arregloPaneles[valor].setStyle("-fx-background-color: green;-fx-border-color: black;");
						arregloIconoVoto[valor].setVisible(true);
						
					}else if(nbre.length()>1){ // asambleista conectado
						arregloLabel[valor].setText(nbre);
						arregloLabel[valor].setTextFill(Color.WHITE);
						arregloPaneles[valor].setStyle("-fx-background-color: green;-fx-border-color: black;");
						arregloIconoVoto[valor].setVisible(false);
					}

				}
			});
		}
		
	}
	
	/**
	 * Método que inicializa los labels del panel donde se muestran los estados de las conexiones
	 */
	private void inicializarPanelAsambleistas(){
		for(int i=0; i<72; i++){

			Image img = new Image(getClass().getResourceAsStream("emitioVoto.png"));
			ImageView imgView = new ImageView(img);
			imgView.setFitHeight(19);
			imgView.setPreserveRatio(true);
			imgView.setVisible(false);

			Label etiqueta = new Label("AUSENTE");
			etiqueta.setTextFill(Color.WHITE);
			etiqueta.setWrapText(true);
			etiqueta.setTextAlignment(TextAlignment.CENTER);
			BorderPane panel  = new BorderPane();
			
			StackPane stack = new StackPane();
			stack.getChildren().add(etiqueta);
			stack.getChildren().add(imgView);
			StackPane.setAlignment(imgView, Pos.BOTTOM_RIGHT);
			
			panel.setCenter(stack);
			panel.setStyle("-fx-background-color: grey;-fx-border-color: black;");
			
			grillaAsambleistas.getChildren().add(panel);

			arregloLabel[i]=etiqueta;
			arregloPaneles[i]=panel;
			arregloIconoVoto[i]=imgView;
		}

	}

	//======= OYENTES del panel de control =======
	
	/**
	 * Handler del botón que inicia la sesión. La conexión ya está establecida, simplemente se envía msj
	 * al server para indicar el inicio de la sesión.
	 * @param e
	 */
	@FXML private void handler_b_iniciar_sesion(Event e){
		Runnable run = new Runnable(){
			public void run(){
				Mensaje msj = new Mensaje();
				msj.setTipo_msj(Mensaje.TIPO_INICIO_SESION);
				Mensaje rta = admin.enviarMSJ(msj);
				if(rta.getTipo_msj().equals(Mensaje.TIPO_OK)){
					Platform.runLater(new Runnable(){public void run(){p_aceptando.toFront();}});
				}
			}
		};
		Thread t=new Thread(run);
		admin.setThread_invocante_actual(t);
		t.start();
	}

	@FXML private void handler_b_detener_conexiones(Event e){

		Runnable run = new Runnable(){// operación dentro de un thread para que no joda a la gui
			public void run(){
				// envío msj para que deje de admitir clientes android
				Mensaje msj = new Mensaje();
				msj.setTipo_msj(Mensaje.TIPO_FIN_ADMISION_CLIENTES);
				Mensaje rta = admin.enviarMSJ(msj);

				if(rta.getTipo_msj().equals(Mensaje.TIPO_OK)){
					System.out.println("# Se dejo de admitir clientes");
					// envío msj para que ver si hay quorum o no
					Mensaje msj2 = new Mensaje();
					msj2.setTipo_msj(Mensaje.TIPO_HAY_QUORUM);

					Mensaje rta2 = admin.enviarMSJ(msj2);
					if(rta2.getTipo_msj().equals(Mensaje.TIPO_SI)){// hay quorum
						System.out.println("# Hay quorum");
						Platform.runLater(new Runnable(){public void run(){p_nuevo_tema.toFront();}});

					}else{// no hay quorum
						System.out.println("# NO hay quorum");
						System.out.println("# msj:"+rta2.getTipo_msj());
						System.out.println("# msj:"+rta2.getContenido());
						Platform.runLater(new Runnable(){
							public void run(){
								l_cant_actual.setText("Cant. Asambleístas Presentes: "+rta2.getContenido());
								p_no_hay_quorum.toFront();
								}
							});

					}
				}
			}
		};
		Thread t=new Thread(run);
		admin.setThread_invocante_actual(t);
		t.start();



	}

	@FXML private void handler_b_iniciar_debate(Event e){
		tema_actual = tf_tema.getText();
		l_tema_actual_iv.setText(tema_actual);
		p_iniciar_votacion.toFront();
	}

	@FXML private void handler_b_iniciar_votacion(Event e){
		Runnable run = new Runnable(){
			public void run(){
				iniciarVotacion();
			}
		};
		Thread t=new Thread(run);
		admin.setThread_invocante_actual(t);
		t.start();

	}
	private void iniciarVotacion(){

		Mensaje msj = new Mensaje();
		msj.setTipo_msj(Mensaje.TIPO_INICIO_TEMA);
		msj.setContenido(tema_actual);
		
		Mensaje rta = admin.enviarMSJ(msj);
		if(rta.getTipo_msj().equals(Mensaje.TIPO_OK)){
			// mostrar verificando quorum....
			System.out.println("#Se inicio tema");
			boolean flag_volver_a_preguntar=false;
			do{
				Mensaje msj2 = new Mensaje();
				msj2.setTipo_msj(Mensaje.TIPO_HAY_VOTACION);
				Mensaje rta2 = admin.enviarMSJ(msj2);
				if(rta2.getTipo_msj().equals(Mensaje.TIPO_SI)){
					// mostrar en votacion
					System.out.println("#Hay votacion");
					flag_volver_a_preguntar=false;
					Platform.runLater(new Runnable(){
						public void run(){
							l_tema_actual_ev.setText(tema_actual);
							p_en_votacion.toFront();
							l_tema_actual_r.setText(tema_actual);							
						}
					});
					boolean flag_volver_a_preguntar2=false;
					do{
						// preguntar por los resultados
						Mensaje msj3 = new Mensaje();
						msj3.setTipo_msj(Mensaje.TIPO_HAY_RTDOS);
						Mensaje rta3 = admin.enviarMSJ(msj3);
						if(rta3.getTipo_msj().equals(Mensaje.TIPO_SI)){
							System.out.println("#Hay rtdos");
							flag_volver_a_preguntar2=false;
							// mostar panel de rtdos
							final int[] rtdos = ParsearRtdos.parsearRtdos(rta3.getContenido());
							Platform.runLater(new Runnable(){
								public void run(){
									int positivos=rtdos[ParsearRtdos.POSITIVOS];
									int negativos=rtdos[ParsearRtdos.NEGATIVOS];
									int abstenciones=rtdos[ParsearRtdos.ABSTENCIONES];
									l_rtdos_p.setText("Positivos: "+positivos);
									l_rtdos_n.setText("Negativos: "+negativos);
									l_rtdos_a.setText("Abstenciones: "+abstenciones);
									if(flag_cierre_sesion){
										botones_rtdos.getChildren().remove(b_cerrar_sesion);
										botones_rtdos.getChildren().add(b_salir);
										l_comment_rtdos.setText("Si se acordó cerrar la sesión, presione \"cierre sesion\". En caso contrario seleccione \"Nuevo Tema\"");
										b_salir.setVisible(true);
									}else{
										botones_rtdos.getChildren().remove(b_salir);
										if(!botones_rtdos.getChildren().contains(b_cerrar_sesion))
											botones_rtdos.getChildren().add(b_cerrar_sesion);
										l_comment_rtdos.setText("A continuacion se muestran los resultados de la votacion. Para continuar puede elegir tratar otro tema o cerrar la sesion (se somete a votacion).");
										b_cerrar_sesion.setVisible(true);
										b_salir.setVisible(false);
									}
									p_rtdos.toFront();
									graficarRtdos(tema_actual, positivos, negativos, abstenciones);
								}
							});
						}else{
							System.out.println("#Aun no hay rtdos");
							flag_volver_a_preguntar2=true;
							// volver a preguntar (poner sleep????)
						}
					}while(flag_volver_a_preguntar2);

				}else if(rta2.getTipo_msj().equals(Mensaje.TIPO_NO)){
					// volver a preguntar
					System.out.println("#Aun no hay votacion");
					flag_volver_a_preguntar=true;
				}else if(rta2.getTipo_msj().equals(Mensaje.TIPO_NO_HAY_QUORUM)){
					System.out.println("#No hay quorum");
					System.out.println("#"+rta2.getContenido());
					Platform.runLater(new Runnable(){
						public void run(){
							l_cant_actual.setText("Cant. Asambleístas Presentes: "+rta2.getContenido());
							p_no_hay_quorum.toFront();
							}
						});
					flag_volver_a_preguntar=false;
				}
			}while(flag_volver_a_preguntar);
		}

	}
	
	/**
	 * Método que construye el gráfico de torta con los resultados
	 * @param tema : string con el tema sometido a votación.
	 * @param positivos : entero con los votos positivos
	 * @param negativos : entero con los votos negativos
	 * @param abstenciones : entero con los votos de abstención
	 */
	private void graficarRtdos(String tema,int positivos, int negativos, int abstenciones){
		ObservableList<PieChart.Data> pieChartData =
				FXCollections.observableArrayList(
						new PieChart.Data("Positivos", positivos),
						new PieChart.Data("Negativos", negativos),
						new PieChart.Data("Abstenciones", abstenciones));
		grafico_rtdos.setData(pieChartData);
		grafico_rtdos.setTitle(tema);
		
	}
	@FXML private void handler_b_nuevo_tema(Event e){
		flag_cierre_sesion=false;
		Runnable run = new Runnable(){
			public void run(){
				Mensaje msj = new Mensaje();
				msj.setTipo_msj(Mensaje.TIPO_NUEVO_TEMA);
				Mensaje rta = admin.enviarMSJ(msj);
				if(rta.getTipo_msj().equals(Mensaje.TIPO_OK)){
					Platform.runLater(new Runnable(){
						public void run(){
							tf_tema.setText("");
							p_nuevo_tema.toFront();
						}
					});

				}else{
					System.err.println("Sucedió un error al enviar Nuevo tema");
				}
			}
		};
		Thread t=new Thread(run);
		admin.setThread_invocante_actual(t);
		t.start();
	}

	@FXML private void handler_b_cerrar_sesion(Event e){
		flag_cierre_sesion=true;
		tema_actual = "Cierre de la Sesion";
		Runnable run = new Runnable(){
			public void run(){
				Mensaje msj = new Mensaje();
				msj.setTipo_msj(Mensaje.TIPO_NUEVO_TEMA);
				Mensaje rta = admin.enviarMSJ(msj);
				if(rta.getTipo_msj().equals(Mensaje.TIPO_OK)){
					iniciarVotacion();
				}else{
					System.err.println("El servidor envío respuesta negativa ante petición de NUEVO TEMA");
				}
			}
		};
		Thread t=new Thread(run);
		admin.setThread_invocante_actual(t);
		t.start();



	}
	@FXML private void handler_b_login(Event e){
		String pass = pass_field.getText();
		pass_field.clear();
		if(admin.verificarPass(pass.toCharArray())){
			pass_valido = pass;
			p_bienvenidos.toFront();
			p_login.setVisible(false);

		}else{
			l_login.setVisible(true);
		}

	}
	@FXML private void handler_b_iniciar_conexion(Event e){
		p_admin.toFront();
		p_login.setVisible(false);

		char[] passKey = pass_valido.toCharArray();
		passKey= (passKey.length<7)? passKey: Arrays.copyOfRange(passKey, 0, 7);
		try{
			moduloConsultas = new ModuloConsultas(pass_valido.toCharArray(), passKey);
			admin.iniciar_conexion(pass_valido.toCharArray(), passKey);

			llenarMapeoSesiones();

			for(EntradaCBox entry: mapeoSesiones.values()){
				cbox_sesion.getItems().add(entry);
			}
			cbox_sesion.getSelectionModel().selectFirst();
			mapeoTemas = new Hashtable<String,EntradaCBox>();
			for(EntradaCBox entry: mapeoTemas.values()){
				cbox_tema.getItems().add(entry);
			}
			// se le agrega el listener al choiceBox (sí, es medio choto, pero el javafx no permite hacerlo de una manera mejor)
			cbox_sesion.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<EntradaCBox>() {
				@Override 
				public void changed(@SuppressWarnings("rawtypes") ObservableValue ov, EntradaCBox viejo, EntradaCBox nuevo) {
					if(nuevo!=null){
						System.out.println("NUEVO: "+nuevo.getValue());
						String idSesion=nuevo.getKey();
						llenarMapeoTemas(idSesion);
						cbox_tema.getSelectionModel().clearSelection();
						cbox_tema.getItems().clear();
						for(EntradaCBox entry: mapeoTemas.values()){
							cbox_tema.getItems().add(entry);
						}
						cbox_tema.getSelectionModel().selectFirst();
					}

				}    
			});
			inicializarPanelAsambleistas();
			actualizarPanelAsambleistas();
		}catch(Exception excp){
			p_error.toFront();
			System.err.println("No se puedo efectuar la conexion con el servidor: "+excp.getMessage());
		}

	}	

	@FXML private void handler_b_salir(Event e){
		
		Runnable run = new Runnable(){
			public void run(){
				Mensaje msj = new Mensaje();
				msj.setTipo_msj(Mensaje.TIPO_CIERRE_SESION);
				admin.enviarMSJ(msj);
			}
		};
		new Thread(run).start();
		
		admin.cerrar_conexion();
		moduloConsultas.cerrar();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			System.err.println(e1.getMessage());
		}
		Platform.exit();
		System.exit(0);
	}
	
	@FXML private void handler_b_consulta_rtdos(Event e){
		EntradaCBox sesion=(EntradaCBox)cbox_sesion.getValue();
		String id_sesion = sesion.getKey();

		if(mapeoTemas.values().size()>0){ // si en la sesion seleccionada hay temas
			EntradaCBox tema = (EntradaCBox)cbox_tema.getValue();
			String idTema= tema.getKey();

			MensajeConsulta msj = new MensajeConsulta(MensajeConsulta.CONSULTA_RTDOS_VOTACION);
			msj.agregarParametro("idSesion", id_sesion);
			msj.agregarParametro("idTema", idTema);
			efectuarConsultaRtdos(msj, sesion.getValue(), tema.getValue());

		}else{
			// NO HAY RTDOS!!!! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
			System.out.println("NO HAY TEMA EN ESTA SESION");
		}

	}
	@FXML private void handler_b_consulta_detalle(Event e){
		EntradaCBox entradaSesion = (EntradaCBox)cbox_sesion.getValue(); 
		String id_sesion = entradaSesion.getKey();

		if(mapeoTemas.values().size()>0){ // si en la sesion seleccionada hay temas
			EntradaCBox entradaTema = (EntradaCBox)cbox_tema.getValue(); 
			String idTema= entradaTema.getKey();

			MensajeConsulta msj = new MensajeConsulta(MensajeConsulta.CONSULTA_VOTOS_CLIENTE);
			msj.agregarParametro("idSesion", id_sesion);
			msj.agregarParametro("idTema", idTema);
			efectuarConsultaVotacion(msj, entradaSesion.getValue(), entradaTema.getValue());

		}else{
			// NO HAY RTDOS!!!! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
			System.out.println("NO HAY TEMA EN ESTA SESION");
		}

	}
	
	@FXML private void handler_b_consulta_clientes(Event e){
		EntradaCBox entrada=(EntradaCBox)cbox_sesion.getValue();
		String id_sesion = entrada.getKey();

		MensajeConsulta msj = new MensajeConsulta(MensajeConsulta.CONSULTA_CLIENTES_DE_SESION);
		msj.agregarParametro("idSesion", id_sesion);
		efectuarConsultaClientes(msj,entrada.getValue());
	}
	
	@FXML private void hanbler_b_consulta_refresh(Event e){
		llenarMapeoSesiones();
		cbox_sesion.getSelectionModel().clearSelection();
		cbox_sesion.getItems().clear();

		if(mapeoSesiones.size()>0){
			for(EntradaCBox entry: mapeoSesiones.values()){
				cbox_sesion.getItems().add(entry);
			}
			cbox_sesion.getSelectionModel().selectFirst();

			String id_sesion = cbox_sesion.getValue().getValue();
			mapeoTemas = llenarMapeoTemas(id_sesion);

			cbox_tema.getSelectionModel().clearSelection();
			cbox_tema.getItems().clear();
			for(EntradaCBox entry: mapeoTemas.values()){
				cbox_tema.getItems().add(entry);
			}
			cbox_tema.getSelectionModel().selectFirst();
		}
	}

	private void llenarMapeoSesiones(){
		mapeoSesiones=new Hashtable<String,EntradaCBox>();
		MensajeConsulta consulta = new MensajeConsulta(MensajeConsulta.CONSULTA_SESIONES);

		moduloConsultas.enviarConsulta(consulta);
		Document rtaConsulta=moduloConsultas.recibirRespuesta();

		Element nodos=rtaConsulta.getDocumentElement();

		NodeList entradas = nodos.getElementsByTagName("entrada");

		// recorrer entradas
		for(int i=0; i<entradas.getLength(); i++){

			NodeList campos = entradas.item(i).getChildNodes();

			String id=campos.item(0).getTextContent();
			String fecha=campos.item(1).getTextContent();

			mapeoSesiones.put(id,new EntradaCBox(id,fecha));
		}

	}

	private Map<String,EntradaCBox> llenarMapeoTemas(String idSesion){
		mapeoTemas=new Hashtable<String,EntradaCBox>();
		MensajeConsulta consulta = new MensajeConsulta(MensajeConsulta.CONSULTA_TEMAS_DE_SESION);
		consulta.agregarParametro("idSesion", idSesion);

		moduloConsultas.enviarConsulta(consulta);
		Document rtaConsulta=moduloConsultas.recibirRespuesta();

		Element nodos=rtaConsulta.getDocumentElement();

		NodeList entradas = nodos.getElementsByTagName("entrada");

		// recorrer entradas
		for(int i=0; i<entradas.getLength(); i++){

			NodeList campos = entradas.item(i).getChildNodes();

			String id=campos.item(0).getTextContent();
			String tema=campos.item(1).getTextContent();

			mapeoTemas.put(id,new EntradaCBox(id,tema));
		}
		return mapeoTemas;

	}	

	private void efectuarConsultaClientes(final MensajeConsulta consulta, String fecha){

		vbox_consultas.setDisable(true);
		Runnable run = new Runnable(){
			public void run(){
				moduloConsultas.enviarConsulta(consulta);
				Document rtaConsulta=moduloConsultas.recibirRespuesta();

				StringBuffer buffer=new StringBuffer();
				ObservableList<CreadorTablas.Cliente> datos = FXCollections.observableArrayList();

				Element nodos=rtaConsulta.getDocumentElement();

				NodeList entradas = nodos.getElementsByTagName("entrada");

				// colocar nbres de columnas
				if(entradas.getLength()>0){
					for(int i=0; i<entradas.getLength(); i++){

						NodeList campos = entradas.item(i).getChildNodes();
						
						Element campoN = (Element) campos.item(0);
						buffer.append(campoN.getTextContent()+"\t\t");

						Element campoA = (Element) campos.item(1);
						buffer.append(campoA.getTextContent()+"\t\t");

						datos.add(new CreadorTablas.Cliente(campoN.getTextContent(),campoA.getTextContent()));

						@SuppressWarnings("rawtypes")
						final TableView tabla = creadorTablas.crearTablaClientes(datos);
						
						buffer.append("\n");
						Platform.runLater(new Runnable(){
							@Override
							public void run() {
								l_consultas_titulo.setText("Resultado de los Asambleistas Presentes en la Sesion del "+fecha);
								panelTabla.getChildren().removeAll(panelTabla.getChildren());
								AnchorPane.setTopAnchor(tabla, 0.0);
								AnchorPane.setBottomAnchor(tabla, 0.0);
								AnchorPane.setLeftAnchor(tabla, 0.0);
								AnchorPane.setRightAnchor(tabla, 0.0);
								panelTabla.getChildren().add(tabla);

							}

						});

					}
				}else{
					buffer.append("La consulta no devolv�o resultados");
				}
				System.out.println(buffer.toString());
				Platform.runLater(new Runnable(){public void run(){vbox_consultas.setDisable(false);}});
			};
		};

		Thread threadConsulta = new Thread(run);
		threadConsulta.start();
	}
	private void efectuarConsultaVotacion(final MensajeConsulta consulta, String fecha, String tema){

		vbox_consultas.setDisable(true);
		Runnable run = new Runnable(){
			public void run(){
				moduloConsultas.enviarConsulta(consulta);
				Document rtaConsulta=moduloConsultas.recibirRespuesta();

				StringBuffer buffer=new StringBuffer();
				ObservableList<CreadorTablas.Voto> datos = FXCollections.observableArrayList();

				Element nodos=rtaConsulta.getDocumentElement();

				NodeList entradas = nodos.getElementsByTagName("entrada");

				// colocar nbres de columnas
				if(entradas.getLength()>0){
					
					for(int i=0; i<entradas.getLength(); i++){

						NodeList campos = entradas.item(i).getChildNodes();

						Element campoN = (Element) campos.item(0);
						buffer.append(campoN.getTextContent()+"\t\t");

						Element campoA = (Element) campos.item(1);
						buffer.append(campoA.getTextContent()+"\t\t");

						Element campoV = (Element) campos.item(2);
						buffer.append(campoV.getTextContent()+"\t\t");

						datos.add(new CreadorTablas.Voto(campoN.getTextContent(),campoA.getTextContent(),campoV.getTextContent()));

						@SuppressWarnings("rawtypes")
						final TableView tabla = creadorTablas.crearTablaDetalles(datos);

						buffer.append("\n");
						Platform.runLater(new Runnable(){
							@Override
							public void run() {
								l_consultas_titulo.setText("Detalle de los votos para el tema \""+tema+"\" en la Sesion del "+fecha);
								panelTabla.getChildren().removeAll(panelTabla.getChildren());
								AnchorPane.setTopAnchor(tabla, 0.0);
								AnchorPane.setBottomAnchor(tabla, 0.0);
								AnchorPane.setLeftAnchor(tabla, 0.0);
								AnchorPane.setRightAnchor(tabla, 0.0);
								panelTabla.getChildren().add(tabla);

							}

						});

					}
				}else{
					buffer.append("La consulta no devolv�o resultados");
				}
				System.out.println(buffer.toString());
				Platform.runLater(new Runnable(){public void run(){vbox_consultas.setDisable(false);}});
			};
		};

		Thread threadConsulta = new Thread(run);
		threadConsulta.start();
	}	
	private void efectuarConsultaRtdos(final MensajeConsulta consulta, String fecha,String tema){

		vbox_consultas.setDisable(true);
		Runnable run = new Runnable(){
			public void run(){
				moduloConsultas.enviarConsulta(consulta);
				Document rtaConsulta=moduloConsultas.recibirRespuesta();

				StringBuffer buffer=new StringBuffer();
				ObservableList<CreadorTablas.Rtdo> datos = FXCollections.observableArrayList();

				Element nodos=rtaConsulta.getDocumentElement();

				NodeList entradas = nodos.getElementsByTagName("entrada");

				// colocar nbres de columnas
				if(entradas.getLength()>0){
					// colocar entradas
					for(int i=0; i<entradas.getLength(); i++){

						NodeList campos = entradas.item(i).getChildNodes();

						Element campoRtdo = (Element) campos.item(0);
						buffer.append(campoRtdo.getTextContent()+"\t\t");

						int[] rtdo = ParsearRtdos.parsearRtdos(campoRtdo.getTextContent());
						System.out.println("#"+rtdo[0]+"\n#"+rtdo[1]+"\n#"+rtdo[2]);
						CreadorTablas.Rtdo entrada = new CreadorTablas.Rtdo(rtdo[ParsearRtdos.POSITIVOS], rtdo[ParsearRtdos.NEGATIVOS], rtdo[ParsearRtdos.ABSTENCIONES]);
						datos.add(entrada);

						@SuppressWarnings("rawtypes")
						final TableView tabla = creadorTablas.crearTablaRtdos(datos);

						buffer.append("\n");
						Platform.runLater(new Runnable(){
							@Override
							public void run() {
								l_consultas_titulo.setText("Resultado de la votacion del tema \""+tema+"\" en la fecha "+fecha);
								panelTabla.getChildren().removeAll(panelTabla.getChildren());
								AnchorPane.setTopAnchor(tabla, 0.0);
								AnchorPane.setBottomAnchor(tabla, 0.0);
								AnchorPane.setLeftAnchor(tabla, 0.0);
								AnchorPane.setRightAnchor(tabla, 0.0);
								panelTabla.getChildren().add(tabla);

							}

						});

					}
				}else{
					buffer.append("La consulta no devolv�o resultados");
				}
				System.out.println(buffer.toString());
				Platform.runLater(new Runnable(){public void run(){vbox_consultas.setDisable(false);}});
			};
		};

		Thread threadConsulta = new Thread(run);
		threadConsulta.start();
	}

	@FXML private void handler_b_cambiar_ip(Event e){
		String ip_nueva = tf_opciones_ip.getText();
		tf_opciones_ip.setText("");
		l_opciones_cambiar_ip.setText("");
		if(validarIP(ip_nueva)){
			if(LectorIpFile.escribirIP(ip_nueva)){
				l_opciones_cambiar_ip.setText("Se cambió el IP correctamente");
				l_opciones_cambiar_ip.setTextFill(Color.GREEN);
			}else{
				l_opciones_cambiar_ip.setText("No se pudo escribir en el archivo de configuración");
				l_opciones_cambiar_ip.setTextFill(Color.RED);
			}

		}else{// no se ingresó una IP (ipv4) válida
			l_opciones_cambiar_ip.setText("No se ingresó una IP válida");
			l_opciones_cambiar_ip.setTextFill(Color.RED);
		}

	}
	@FXML private void handler_b_volver(Event e){// <<<<<<<<<<<<<<<<<<<<<<<< agregarlo al botonazo!!!!!!!!!!!!
		p_bienvenidos.toFront();
	}
	@FXML private void handler_b_opciones(Event e){
		p_opciones.toFront();
	}
	
	/**
	 * Método que valida si el string recibido es una ip valida mediante espresiones regulares.
	 * @param ip : string con ip
	 * @return true si es una ip valida, false caso contrario
	 */
	private boolean validarIP(String ip){
		String PATRON_IP = 
				"^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
						"([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
						"([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
						"([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";

		Pattern patronIP = Pattern.compile(PATRON_IP);
		Matcher matcher = patronIP.matcher(ip);
		return matcher.matches();
	}
	
	@FXML private void handler_b_ambiar_pass(Event e){
		String pass_actual = pf_opciones_pass_actual.getText();
		String pass_nuevo1 = pf_opciones_pass_nuevo1.getText();
		String pass_nuevo2 = pf_opciones_pass_nuevo2.getText();

		l_opciones_msj_pass.setText("");
		pf_opciones_pass_actual.clear();
		pf_opciones_pass_nuevo1.clear();
		pf_opciones_pass_nuevo2.clear();
		if(pass_actual.length()==0 || pass_nuevo1.length()==0 || pass_nuevo2.length()==0){
			// algún campo vacio
			l_opciones_msj_pass.setTextFill(Color.RED);
			l_opciones_msj_pass.setText("Alguno de los campos está vacio");
		}else if(!ClienteAdmin.verificarPassKS(pass_actual.toCharArray())){
			// el pass actual no es valido
			l_opciones_msj_pass.setTextFill(Color.RED);
			l_opciones_msj_pass.setText("El pass actual no es valido");
		}else if(!pass_nuevo1.equals(pass_nuevo2)){
			// los pass nuevos no coinciden entre si
			l_opciones_msj_pass.setTextFill(Color.RED);
			l_opciones_msj_pass.setText("El pass nuevo no coincide con el reingresado.");
		}else if(pass_nuevo1.length()<8 || pass_nuevo1.length()>16){
			// passwords de logitud no permitida
			l_opciones_msj_pass.setTextFill(Color.RED);
			l_opciones_msj_pass.setText("Longitud no permitida para el pass nuevo");
		}else{// ahora sí!
			// cambiar password
			ClienteAdmin.cambiarPassKS(pass_actual.toCharArray(), pass_nuevo1.toCharArray());
			l_opciones_msj_pass.setTextFill(Color.GREEN);
			l_opciones_msj_pass.setText("El password se cambio correctamente");
			pass_valido=pass_nuevo1;
		}
	}
	

	@FXML private void handler_b_cerrar(Event e){
		if(moduloConsultas!=null)
			moduloConsultas.cerrar();
		try {
			Thread.sleep(500);
		} catch (InterruptedException e1) {
			//e1.printStackTrace();
			System.err.println(e1.getMessage());
		}
		Platform.exit();
		System.exit(0);
	}
	public void mostrar_error_conexion(){
		Platform.runLater(new Runnable(){
			public void run(){
				p_error.toFront();
			}
		});
	}
	/**
	 * Clase privada para representar las entradas en los combobox del panel de consultas 
	 */
	private class EntradaCBox{
		private String key;
		private String value;
		public EntradaCBox(String k, String v){
			setKey(k);
			setValue(v); 
		}
		public String getKey() {
			return key;
		}
		public String getValue() {
			return value;
		}
		public void setValue(String value) {
			this.value = value;
		}
		public String toString() {
			return value;
		}
		public void setKey(String key) {
			this.key = key;
		}
	}	

}